package IdGenerator

import (
	"bytes"
	"sync"
	"time"
)

type StringIdGenerator interface {
	Next() string
}

type stringIdGenerator struct {
	nidStr string
	len    int
	count  int16
	p      int16
	t      *time.Ticker
	now    int64
	sync.Mutex
}

const stringTable = "234hijklmnuvwABCDEFGab01cdefgH56789IJKLMNOPQRSTopqrstUVWXYZxyz#*"

func NewStringIdGenerator(nid int16, len int) StringIdGenerator {
	if len > 14 || len < 5 {
		panic("len must be in [5,14]")
	}
	var buffer bytes.Buffer
	nid *= 3
	buffer.WriteByte(stringTable[(nid>>3)&0x7+(nid>>9)&0x38])
	buffer.WriteByte(stringTable[(nid>>1)&0x7+(nid>>6)&0x38])
	buffer.WriteByte(stringTable[nid&0x7+(nid>>3)&0x38])
	t := time.NewTicker(time.Second)
	gen := &stringIdGenerator{
		nidStr: buffer.String(),
		len:    len,
		t:      t,
		p:      0,
		count:  0,
		now:    time.Now().Unix(),
	}
	go func() {
		select {
		case <-t.C:
			gen.Lock()
			gen.count = 0
			time.Now().Unix()
			gen.Unlock()
		}
	}()
	return gen
}

func (s *stringIdGenerator) Next() string {
	for s.count >= 4095 {
		<-time.NewTimer(1 * time.Millisecond).C
	}
	s.Lock()
	defer s.Unlock()
	s.count++
	s.p++
	n := time.Now().UnixMicro()
	buffer := bytes.NewBufferString(s.nidStr)
	for i := 0; i < s.len-5; i++ {
		buffer.WriteByte(stringTable[n&0x3f])
		n >>= 6
	}
	buffer.WriteByte(stringTable[(s.p>>6)&0x3f])
	buffer.WriteByte(stringTable[s.p&0x3f])
	return buffer.String()
}
